package cn.com.flever.utils;

import cn.com.flever.entity.PomModel;
import cn.hutool.core.io.FileUtil;
import com.alibaba.fastjson.JSONObject;
import com.google.common.base.CaseFormat;
import com.intellij.openapi.ui.Messages;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.http.*;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import java.io.*;
import java.nio.charset.Charset;
import java.util.*;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

/**
 * 代码生成器工具类
 */
public class GenerateUtils {

    //代码生成请求url
    private static final String GEN_URL = "http://***/toud2/gen?token=tc_market_token";

    //代码下载地址
    private static String DOWNLOAD_URL = "http://***toud2/download?token=#{ID}";

    //PDM上传地址
    private static String UPLOAD_URL = "http://***/toud2/upload";

    //单测代码git地址
    private static String UNIT_TEST_DOWNLOAD_URL="https://***/unitest.git";


    /**
     * 生成full model
     *
     * @param generatePath 生成路径
     * @param packagePath  生成的包名
     * @param pdmPath      pdm路径
     * @param pomModel     pom.xml模型
     */
    public static void getFullModel(String generatePath, String packagePath, String pdmPath, PomModel pomModel) {
        //生成代码文件
        generatePath = generatePath.substring(0, generatePath.indexOf("/src/"));
        File fullModel = send(packagePath, pdmPath, pomModel, "model-basic", generatePath);
        //文件解压缩
        unZip(fullModel, generatePath + "/");
    }

    /**
     * 发送代码生成请求
     *
     * @param packagePath
     * @param pdmPath
     * @param pomModel
     * @param type
     * @return
     */
    public static File send(String packagePath, String pdmPath, PomModel pomModel, String type, String converPdmPath) {
        //填充参数
        RestTemplate restTemplate = new RestTemplate();
        MultiValueMap<String, Object> paramMap = new LinkedMultiValueMap<>();
        paramMap.add("name", pomModel.getArtifactId());
        paramMap.add("code", pomModel.getName());
        paramMap.add("base_package", packagePath);
        paramMap.add("mode", type);
        //填充pdm文件对象
        File file = new File(pdmPath);
        paramMap.add("file_token", upload(file,converPdmPath));
        try {
            //http调用
            JSONObject json = restTemplate.postForObject(GEN_URL, paramMap, JSONObject.class);
            //判断请求是否成功
            if (Boolean.TRUE.equals(json.get("succ"))) {
                //下载zip文件
                File zip = downLoadFile(json.get("result").toString());
                return zip;
            }
            else{
                Messages.showErrorDialog(
                        json.get("message").toString(),
                        "代码生成错误");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 上传PDM文件
     * @param file
     * @param converPdmPath
     * @return
     */
    public static String upload(File file, String converPdmPath) {
        //填充参数
        RestTemplate restTemplate = new RestTemplate();
        MultiValueMap<String, Object> paramMap = new LinkedMultiValueMap<>();
        //填充pdm文件对象
        //对pdm文件进行驼峰转换,并在当前目录生成
        File converFile = new File(converPdmPath + File.separator + file.getName());
        file = CamelCaseConvert(file, converFile);
        byte[] bytesArray = new byte[(int) file.length()];
        try {
            FileInputStream fis = fis = new FileInputStream(file);
            fis.read(bytesArray); //read file into bytes[]
            fis.close();
            ByteArrayResource contentsAsResource = new ByteArrayResource(bytesArray) {
                @Override
                public String getFilename() {
                    return "file.pdm";
                }
            };
            paramMap.add("file", contentsAsResource);
            //http调用
            JSONObject json = restTemplate.postForObject(UPLOAD_URL, paramMap, JSONObject.class);
            //判断请求是否成功
            if (Boolean.TRUE.equals(json.get("succ"))) {
                return json.get("result").toString();
            }
            else{
                Messages.showErrorDialog(
                        json.get("message").toString(),
                        "上传PDM文件出错");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 下载zip代码文件
     *
     * @param id 文件id
     * @return
     */
    public static File downLoadFile(String id) {
        //设置下载参数
        String tmpUrl = DOWNLOAD_URL.replace("#{ID}", id);
        RestTemplate restTemplate = new RestTemplate();
        final String APPLICATION_ZIP = "application/zip";
        HttpHeaders headers = new HttpHeaders();
        OutputStream outputStream = null;
        InputStream inputStream = null;
        try {
            ArrayList list = new ArrayList<String>();
            list.add(MediaType.valueOf(APPLICATION_ZIP));
            headers.setAccept(list);
            //发初请求并获取byte流
            ResponseEntity<byte[]> response = restTemplate.exchange(
                    tmpUrl,
                    HttpMethod.GET,
                    new HttpEntity<byte[]>(headers),
                    byte[].class);

            byte[] result = response.getBody();
            inputStream = new ByteArrayInputStream(result);
            //写入file
            File file = new File(id + ".zip");
            if (!file.exists()) {
                file.createNewFile();
            }
            outputStream = new FileOutputStream(file);
            int len = 0;
            byte[] buf = new byte[1024];
            while ((len = inputStream.read(buf, 0, 1024)) != -1) {
                outputStream.write(buf, 0, len);
            }
            outputStream.flush();
            return file;

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (inputStream != null)
                    inputStream.close();
                if (outputStream != null)
                    outputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    /**
     * 解压文件
     *
     * @param srcFile     源文件
     * @param destDirPath 解压路径
     * @throws RuntimeException
     */
    public static void unZip(File srcFile, String destDirPath) throws RuntimeException {
        long start = System.currentTimeMillis();
        if (!srcFile.exists()) {
            throw new RuntimeException(srcFile.getPath() + "file can not found");
        }
        ZipFile zipFile = null;
        try {
            zipFile = new ZipFile(srcFile);
            Enumeration<?> entries = zipFile.entries();
            //遍历压缩包进行解压
            while (entries.hasMoreElements()) {
                ZipEntry entry = (ZipEntry) entries.nextElement();
                //如果是目录则创建
                if (entry.isDirectory()) {
                    String dirPath = destDirPath + "/" + entry.getName();
                    File dir = new File(dirPath);
                    dir.mkdirs();
                } else {
                    //如果是文件则解压
                    File targetFile = new File(destDirPath + "/" + entry.getName());
                    File sourceFile =null;
                    //如果是messages-entity.properties文件或者genMapper文件则复制出备份
                    if ((entry.getName().contains("messages-entity.properties")||mergeMapperOrNot(entry.getName(),targetFile))&&targetFile.exists()) {
                        sourceFile = new File(UUID.randomUUID().toString()+targetFile.getName());
                        if(!sourceFile.exists())
                        {
                            sourceFile.createNewFile();
                        }
                        sourceFile = FileUtil.copy(targetFile, sourceFile, true);
                    }
                    //判断如果service文件当前已存在则不覆盖
                    if (unzipServiceOrNot(entry.getName(), targetFile)) {
                        continue;
                    }
                    if(unzipMapperOrNot(entry.getName(), targetFile))
                    {
                        continue;
                    }
                    if (!targetFile.getParentFile().exists()) {
                        targetFile.getParentFile().mkdirs();
                    }

                    targetFile.createNewFile();
                    InputStream is = zipFile.getInputStream(entry);
                    FileOutputStream fos = new FileOutputStream(targetFile);
                    int len;
                    byte[] buf = new byte[1024];
                    while ((len = is.read(buf)) != -1) {
                        fos.write(buf, 0, len);
                    }
                    // 关流顺序，先打开的后关闭
                    fos.close();
                    is.close();
                    //如果是messages-entity.properties文件则合并
                    if (entry.getName().contains("messages-entity.properties")&&targetFile.exists()) {
                        ArrayList<File> files = new ArrayList<>();
                        //原本的文件放在前面，用作reader的编码读取
                        files.add(sourceFile);
                        files.add(targetFile);
                        String encode = checkEncode(sourceFile);
                        Writer(Reader(files,encode), targetFile,encode);
                        sourceFile.delete();
                    }
                    if(mergeMapperOrNot(entry.getName(),targetFile))
                    {
                        //如果是genMaper.xml则合并xml文件
                        MapperUtil.MergeMapperXml(sourceFile,targetFile);
                    }
                }
            }
            long end = System.currentTimeMillis();
            System.out.println("unzip complete use:" + (end - start) + " ms");
            //删除下载的文件
            srcFile.delete();
        } catch (Exception e) {
            throw new RuntimeException("unzip error from ZipUtils", e);
        } finally {
            if (zipFile != null) {
                try {
                    zipFile.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 判断是不是service文件，且不是genService文件，如果文件是service文件且存在则不覆盖原本的文件
     *
     * @param fileName
     * @param targetFile
     * @return
     */
    public static boolean unzipServiceOrNot(String fileName, File targetFile) {
        String pattern = ".*/service/.*Service.java";
        boolean isMatch = Pattern.matches(pattern, fileName);
        String pattern1 = ".*/service/.*GenService.java";
        boolean isMatch1 = Pattern.matches(pattern1, fileName);
        return isMatch && (!isMatch1) && targetFile.exists();
    }

    /**
     * 判断是不是Mapper文件，且不是genMapper文件，如果文件是Mapper文件且存在则不覆盖原本的文件
     *
     * @param fileName
     * @param targetFile
     * @return
     */
    public static boolean unzipMapperOrNot(String fileName, File targetFile) {
        String pattern = ".*/mapper/.*Mapper.java";
        boolean isMatch = Pattern.matches(pattern, fileName);
        String pattern1 = ".*/mapper/.*GenMapper.java";
        boolean isMatch1 = Pattern.matches(pattern1, fileName);
        return isMatch && (!isMatch1) && targetFile.exists();
    }

    /**
     * 判断是不是GenMapper.xml文件，如果文件是GenMapper.xml文件且存在则合并mapper
     *
     * @param fileName
     * @param targetFile
     * @return
     */
    public static boolean mergeMapperOrNot(String fileName, File targetFile) {
        String pattern1 = ".*/mapper/.*GenMapper.xml";
        boolean isMatch1 = Pattern.matches(pattern1, fileName);
        return isMatch1 && targetFile.exists();
    }

    /**
     * 合并国际化文件
     *
     * @param fileList
     * @return
     */
    public static Set<String> Reader(ArrayList<File> fileList,String encode) {
        LinkedHashSet<String> setStr = new LinkedHashSet<String>();
        try {
            //获得源文件的编码格式
            for (File fl : fileList) {
                //创建BufferedInputStream 对象
                BufferedReader bytesRead=new BufferedReader(new InputStreamReader(new FileInputStream(fl),encode));
                String line = null;
                while ((line = bytesRead.readLine()) != null) {
                    //将读取的字节转为字符串对象
                    if (!line.trim().equals("")) {
                        setStr.add(line);
                    }
                }
                bytesRead.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return setStr;
    }

    /**
     * 生成新的文件
     *
     * @param str
     * @param file
     * @throws IOException
     */
    public static void Writer(Set<String> str, File file,String encode) throws IOException {
        OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream(file), encode);
        int count = 0;
        for (String string : str) {
            if (!string.trim().equals("")) {
                count++;
                if(string.contains("#"))
                {
                    out.write( "\r\n");
                }
                out.write(string.trim() + "\r\n");

            }
        }
        out.flush();
        out.close();
    }

    /**
     * 驼峰转换
     * @param pdmFile
     * @param outputFile
     * @return
     */
    public static File CamelCaseConvert(File pdmFile, File outputFile) {
        try {
            SAXReader reader = new SAXReader();
            Document document = reader.read(new BufferedInputStream(new FileInputStream(pdmFile)));
            String encode= document.getXMLEncoding();
            List<Node> nodes = document.selectNodes("/Model/o:RootObject/c:Children/o:Model/c:Tables/o:Table/c:Columns/o:Column");
            for (Node node : nodes) {
                Node nameNode = node.selectSingleNode("a:Name");
                Node codeNode = node.selectSingleNode("a:Code");
                String columnCode = codeNode.getText();

                String tmp = CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, columnCode);

                codeNode.setText(tmp);

                tmp = CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, columnCode);

                nameNode.setText(tmp);
            }
            OutputFormat format = OutputFormat.createPrettyPrint();
            //6.//创建一个xml文件
            OutputStream out = new FileOutputStream(outputFile);
            Writer wr = new OutputStreamWriter(out, encode);//用可改变编码的OutputStreamWriter代替了普通的FileWriter解决中文乱码问题
            XMLWriter output = new XMLWriter(wr,format);
            //7.将doc输出到xml文件中
            output.write(document);
            //8.关闭资源
            wr.close();
            out.close();
            output.close();
            return outputFile;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;

    }



    public static String checkEncode(File file)  {
        String charset = "GBK";
        byte[] first3Bytes = new byte[3];
        try {
            boolean checked = false;
            BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
            bis.mark(0);
            int read = bis.read(first3Bytes, 0, 3);
            if (read == -1) {
                return charset; //文件编码为 ANSI
            } else if (first3Bytes[0] == (byte) 0xFF
                    && first3Bytes[1] == (byte) 0xFE) {
                charset = "UTF-16LE"; //文件编码为 Unicode
                checked = true;
            } else if (first3Bytes[0] == (byte) 0xFE
                    && first3Bytes[1] == (byte) 0xFF) {
                charset = "UTF-16BE"; //文件编码为 Unicode big endian
                checked = true;
            } else if (first3Bytes[0] == (byte) 0xEF
                    && first3Bytes[1] == (byte) 0xBB
                    && first3Bytes[2] == (byte) 0xBF) {
                charset = "UTF-8"; //文件编码为 UTF-8
                checked = true;
            }
            bis.reset();
            if (!checked) {
                int loc = 0;
                while ((read = bis.read()) != -1) {
                    loc++;
                    if (read >= 0xF0)
                        break;
                    if (0x80 <= read && read <= 0xBF) // 单独出现BF以下的，也算是GBK
                        break;
                    if (0xC0 <= read && read <= 0xDF) {
                        read = bis.read();
                        if (0x80 <= read && read <= 0xBF) // 双字节 (0xC0 - 0xDF)
                            // (0x80
                            // - 0xBF),也可能在GB编码内
                            continue;
                        else
                            break;
                    } else if (0xE0 <= read && read <= 0xEF) {// 也有可能出错，但是几率较小
                        read = bis.read();
                        if (0x80 <= read && read <= 0xBF) {
                            read = bis.read();
                            if (0x80 <= read && read <= 0xBF) {
                                charset = "UTF-8";
                                break;
                            } else
                                break;
                        } else
                            break;
                    }
                }
            }
            bis.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return charset;
    }


    public static File downloadForGit(String path)
    {
        String rootPath=path+File.separator+"unit_test_tmp"+File.separator;
        File file = new File(rootPath);
        if(!file.exists())
        {
            file.mkdir();
        }
        String command = String.format("git clone -b %s %s ", "download", UNIT_TEST_DOWNLOAD_URL);
        System.out.println("start exec command : " + command);
        try {
            Process exec = Runtime.getRuntime().exec(command, null,file);
            exec.waitFor();
            unZip(new File(rootPath+"unitest"+File.separator+"test.zip"),path);
            deleteDirectory(rootPath);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 删除目录（文件夹）以及目录下的文件
     * @param   sPath 被删除目录的文件路径
     * @return  目录删除成功返回true，否则返回false
     */
    public static boolean deleteDirectory(String sPath) {
        //如果sPath不以文件分隔符结尾，自动添加文件分隔符
        if (!sPath.endsWith(File.separator)) {
            sPath = sPath + File.separator;
        }
        File dirFile = new File(sPath);
        //如果dir对应的文件不存在，或者不是一个目录，则退出
        if (!dirFile.exists() || !dirFile.isDirectory()) {
            return false;
        }
        boolean flag = true;
        //删除文件夹下的所有文件(包括子目录)
        File[] files = dirFile.listFiles();
        for (int i = 0; i < files.length; i++) {
            //删除子文件
            if (files[i].isFile()) {
                flag = deleteFile(files[i].getAbsolutePath());
                if (!flag) break;
            } //删除子目录
            else {
                flag = deleteDirectory(files[i].getAbsolutePath());
                if (!flag) break;
            }
        }
        if (!flag) return false;
        //删除当前目录
        if (dirFile.delete()) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * 删除单个文件
     * @param   sPath    被删除文件的文件名
     * @return 单个文件删除成功返回true，否则返回false
     */
    public static boolean deleteFile(String sPath) {
        boolean flag = false;
        File  file = new File(sPath);
        // 路径为文件且不为空则进行删除
        if (file.isFile() && file.exists()) {
            file.delete();
            flag = true;
        }
        return flag;
    }

    public static void main(String[] args) throws IOException {

        downloadForGit("C://");


      /*  ArrayList<File> fileList=new ArrayList<>();
        fileList.add(new File("E:\\workspace\\ssab\\src\\main\\resources\\messages-entity.properties"));
        fileList.add(new File("D:\\messages-entity.properties"));
        Writer(Reader(fileList),new File("E:\\1.properties"));*/
    }
}

